<!-- eslint-disable -->
<template>
	<view class="player-wrapper" :id="videoWrapperId" :parentId="id" :randomNum="randomNum"
		:change:randomNum="domVideoPlayer.randomNumChange" :viewportProps="viewportProps"
		:change:viewportProps="domVideoPlayer.viewportChange" :videoSrc="videoSrc"
		:change:videoSrc="domVideoPlayer.initVideoPlayer" :command="eventCommand"
		:change:command="domVideoPlayer.triggerCommand" :func="renderFunc" :change:func="domVideoPlayer.triggerFunc" />
</template>

<script>
	export default {
		props: {
			src: {
				type: String,
				default: ''
			},
			autoplay: {
				type: Boolean,
				default: false
			},
			loop: {
				type: Boolean,
				default: false
			},
			controls: {
				type: Boolean,
				default: false
			},
			objectFit: {
				type: String,
				default: 'contain'
			},
			muted: {
				type: Boolean,
				default: false
			},
			playbackRate: {
				type: Number,
				default: 1
			},
			isLoading: {
				type: Boolean,
				default: false
			},
			poster: {
				type: String,
				default: ''
			},
			id: {
				type: String,
				default: ''
			}
		},
		data() {
			return {
				randomNum: Math.floor(Math.random() * 100000000),
				videoSrc: '',
				// 父组件向子组件传递的事件指令（video的原生事件）
				eventCommand: null,
				// 父组件传递过来的，对 renderjs 层的函数执行（对视频控制的自定义事件）
				renderFunc: {
					name: null,
					params: null
				},
				// 提供给父组件进行获取的视频属性
				currentTime: 0,
				duration: 0,
				playing: false
			}
		},
		watch: {
			// 监听视频资源地址更新
			src: {
				handler(val) {
					if (!val) return
					setTimeout(() => {
						this.videoSrc = val
					}, 0)
				},
				immediate: true
			}
		},
		computed: {
			videoWrapperId() {
				return `video-wrapper-${this.randomNum}`
			},
			// 聚合视图层的所有数据变化，传给renderjs的渲染层
			viewportProps() {
				return {
					autoplay: this.autoplay,
					muted: this.muted,
					controls: this.controls,
					loop: this.loop,
					objectFit: this.objectFit,
					poster: this.poster,
					isLoading: this.isLoading,
					playbackRate: this.playbackRate
				}
			}
		},
		// 方法
		methods: {
			// 传递事件指令给父组件
			eventEmit({
				event,
				data
			}) {
				this.$emit(event, data)
			},
			// 修改view视图层的data数据
			setViewData({
				key,
				value
			}) {
				key && this.$set(this, key, value)
			},
			// 重置事件指令
			resetEventCommand() {
				this.eventCommand = null
			},
			// 播放指令
			play() {
				if (this.playing) return;
				this.eventCommand = 'play';
				this.playing = true;
			},
			// 暂停指令
			pause() {
				if (!this.playing) return;
				this.eventCommand = 'pause';
				this.playing = false;
			},
			// 重置自定义函数指令
			resetFunc() {
				this.renderFunc = {
					name: null,
					params: null
				}
			},
			// 自定义函数 - 移除视频
			remove(params) {
				this.renderFunc = {
					name: 'removeHandler',
					params
				}
			},
			// 自定义函数 - 全屏播放
			fullScreen(params) {
				this.renderFunc = {
					name: 'fullScreenHandler',
					params
				}
			},
			// 自定义函数 - 跳转到指定时间点
			toSeek(sec, isDelay = false) {
				this.renderFunc = {
					name: 'toSeekHandler',
					params: {
						sec,
						isDelay
					}
				}
			}
		}
	}
</script>


<script module="domVideoPlayer" lang="renderjs">
	const PLAYER_ID = 'DOM_VIDEO_PLAYER'
	export default {
		data() {
			return {
				num: '',
				videoEl: null,
				loadingEl: null,
				// 延迟生效的函数
				delayFunc: null,
				renderProps: {}
			}
		},
		computed: {
			playerId() {
				return `${PLAYER_ID}_${this.num}`
			},
			wrapperId() {
				return `video-wrapper-${this.num}`
			}
		},
		methods: {
			isApple() {
				const ua = navigator.userAgent.toLowerCase()
				return ua.indexOf('iphone') !== -1 || ua.indexOf('ipad') !== -1
			},
			async initVideoPlayer(src) {
				this.delayFunc = null
				await this.$nextTick()
				if (!src) return
				if (this.videoEl) {
					// 切换视频源
					if (!this.isApple() && this.loadingEl) {
						this.loadingEl.style.display = 'block'
					}
					this.videoEl.src = src
					return
				}

				const videoEl = document.createElement('video')
				this.videoEl = videoEl
				// 开始监听视频相关事件
				this.listenVideoEvent()
				
				const {
					autoplay,
					muted,
					controls,
					loop,
					playbackRate,
					objectFit,
					poster
				} = this.renderProps
				poster && (videoEl.poster = poster)
				videoEl.src = src
				videoEl.autoplay = autoplay
				videoEl.controls = controls
				videoEl.loop = loop
				videoEl.muted = muted
				videoEl.playbackRate = playbackRate
				videoEl.id = this.playerId
				// videoEl.setAttribute('x5-video-player-type', 'h5')
				videoEl.setAttribute('preload', 'auto')
				videoEl.setAttribute('playsinline', true)
				videoEl.setAttribute('webkit-playsinline', true)
				videoEl.setAttribute('crossorigin', 'anonymous')
				videoEl.setAttribute('controlslist', 'nodownload')
				videoEl.setAttribute('disablePictureInPicture', true)
				videoEl.style.objectFit = objectFit
				// poster && (videoEl.poster = poster)
				videoEl.style.width = '100%'
				videoEl.style.height = '100%'

				// 插入视频元素
				// document.getElementById(this.wrapperId).appendChild(videoEl)
				const playerWrapper = document.getElementById(this.wrapperId)
				playerWrapper.insertBefore(videoEl, playerWrapper.firstChild)

				// 插入loading 元素（遮挡安卓的默认加载过程中的黑色播放按钮）
				this.createLoading()
			},
			// 创建 loading
			createLoading() {
				const {
					isLoading
				} = this.renderProps
				if (!this.isApple() && isLoading) {
					const loadingEl = document.createElement('div')
					this.loadingEl = loadingEl
					loadingEl.className = 'loading-wrapper'
					loadingEl.style.position = 'absolute'
					loadingEl.style.top = '0'
					loadingEl.style.left = '0'
					loadingEl.style.zIndex = '1'
					loadingEl.style.width = '100%'
					loadingEl.style.height = '100%'
					loadingEl.style.backgroundColor = 'black'
					document.getElementById(this.wrapperId).appendChild(loadingEl)

					// 创建 loading 动画
					const animationEl = document.createElement('div')
					animationEl.className = 'loading'
					animationEl.style.zIndex = '2'
					animationEl.style.position = 'absolute'
					animationEl.style.top = '50%'
					animationEl.style.left = '50%'
					animationEl.style.marginTop = '-15px'
					animationEl.style.marginLeft = '-15px'
					animationEl.style.width = '30px'
					animationEl.style.height = '30px'
					animationEl.style.border = '2px solid #FFF'
					animationEl.style.borderTopColor = 'rgba(255, 255, 255, 0.2)'
					animationEl.style.borderRightColor = 'rgba(255, 255, 255, 0.2)'
					animationEl.style.borderBottomColor = 'rgba(255, 255, 255, 0.2)'
					animationEl.style.borderRadius = '100%'
					animationEl.style.animation = 'circle infinite 0.75s linear'
					loadingEl.appendChild(animationEl)

					// 创建 loading 动画所需的 keyframes
					const style = document.createElement('style')
					const keyframes = `
						@keyframes circle {
							0% {
							  transform: rotate(0);
							}
							100% {
							  transform: rotate(360deg);
							}
						  }
						`
					style.type = 'text/css'
					if (style.styleSheet) {
						style.styleSheet.cssText = keyframes
					} else {
						style.appendChild(document.createTextNode(keyframes))
					}
					document.head.appendChild(style)
				}
			},
			// 监听视频相关事件
			listenVideoEvent() {
				// 播放事件监听
				const playHandler = () => {
					this.$ownerInstance.callMethod('eventEmit', {
						event: 'play'
					});
					this.$ownerInstance.callMethod('setViewData', {
						key: 'playing',
						value: true
					});

					if (this.loadingEl) {
						this.loadingEl.style.display = 'none';
					}
				};
				this.videoEl && this.videoEl.removeEventListener('play', playHandler);
				this.videoEl && this.videoEl.addEventListener('play', playHandler);

				// 暂停事件监听
				const pauseHandler = () => {
					this.$ownerInstance.callMethod('eventEmit', {
						event: 'pause'
					})
					this.$ownerInstance.callMethod('setViewData', {
						key: 'playing',
						value: false
					})
				}
				this.videoEl.removeEventListener('pause', pauseHandler)
				this.videoEl.addEventListener('pause', pauseHandler)

				// 结束事件监听
				const endedHandler = () => {
					this.$ownerInstance.callMethod('eventEmit', {
						event: 'ended'
					})
					this.$ownerInstance.callMethod('resetEventCommand')
				}
				this.videoEl.removeEventListener('ended', endedHandler)
				this.videoEl.addEventListener('ended', endedHandler)

				// 加载完成事件监听
				const canPlayHandler = () => {
					this.$ownerInstance.callMethod('eventEmit', {
						event: 'canplay'
					})
					this.execDelayFunc()
				}
				this.videoEl.removeEventListener('canplay', canPlayHandler)
				this.videoEl.addEventListener('canplay', canPlayHandler)

				// 加载失败事件监听
				const errorHandler = (e) => {
					if (this.loadingEl) {
						this.loadingEl.style.display = 'block'
					}
					this.$ownerInstance.callMethod('eventEmit', {
						event: 'error'
					})
				}
				this.videoEl.removeEventListener('error', errorHandler)
				this.videoEl.addEventListener('error', errorHandler)

				// loadedmetadata 事件监听
				const loadedMetadataHandler = () => {
					this.$ownerInstance.callMethod('eventEmit', {
						event: 'loadedmetadata'
					})
					// 获取视频的长度
					const duration = this.videoEl.duration
					this.$ownerInstance.callMethod('eventEmit', {
						event: 'durationchange',
						data: duration
					})

					this.$ownerInstance.callMethod('setViewData', {
						key: 'duration',
						value: duration
					})

					// 加载首帧视频 模拟出封面图
					this.loadFirstFrame()
				}
				this.videoEl.removeEventListener('loadedmetadata', loadedMetadataHandler)
				this.videoEl.addEventListener('loadedmetadata', loadedMetadataHandler)

				// 播放进度监听
				const timeupdateHandler = (e) => {
					const currentTime = e.target.currentTime
					this.$ownerInstance.callMethod('eventEmit', {
						event: 'timeupdate',
						data: currentTime
					})

					this.$ownerInstance.callMethod('setViewData', {
						key: 'currentTime',
						value: currentTime
					})

				}
				this.videoEl.removeEventListener('timeupdate', timeupdateHandler)
				this.videoEl.addEventListener('timeupdate', timeupdateHandler)

				// 倍速播放监听
				const ratechangeHandler = (e) => {
					const playbackRate = e.target.playbackRate
					this.$ownerInstance.callMethod('eventEmit', {
						event: 'ratechange',
						data: playbackRate
					})
				}
				this.videoEl.removeEventListener('ratechange', ratechangeHandler)
				this.videoEl.addEventListener('ratechange', ratechangeHandler)

				// 全屏事件监听
				if (this.isApple()) {
					const webkitbeginfullscreenHandler = () => {
						const presentationMode = this.videoEl.webkitPresentationMode
						let isFullScreen = null
						if (presentationMode === 'fullscreen') {
							isFullScreen = true
						} else {
							isFullScreen = false
						}
						this.$ownerInstance.callMethod('eventEmit', {
							event: 'fullscreenchange',
							data: isFullScreen
						})
					}
					this.videoEl.removeEventListener('webkitpresentationmodechanged', webkitbeginfullscreenHandler)
					this.videoEl.addEventListener('webkitpresentationmodechanged', webkitbeginfullscreenHandler)
				} else {
					const fullscreenchangeHandler = () => {
						let isFullScreen = null
						if (document.fullscreenElement) {
							isFullScreen = true
						} else {
							isFullScreen = false
						}
						this.$ownerInstance.callMethod('eventEmit', {
							event: 'fullscreenchange',
							data: isFullScreen
						})
					}
					document.removeEventListener('fullscreenchange', fullscreenchangeHandler)
					document.addEventListener('fullscreenchange', fullscreenchangeHandler)
				}
			},
			// 加载首帧视频，模拟出封面图
			loadFirstFrame() {
				let {
					autoplay,
					muted
				} = this.renderProps
				if (this.isApple()) {
					this.videoEl.play()
					if (!autoplay) {
						this.videoEl.pause()
					}
				} else {
					// optimize: timeout 延迟调用是为了规避控制台的`https://goo.gl/LdLk22`这个报错
					/**
					 * 原因：chromium 内核中，谷歌协议规定，视频不允许在非静音状态下进行自动播放
					 * 解决：在自动播放时，先将视频静音，然后延迟调用 play 方法，播放视频
					 * 说明：iOS 的 Safari 内核不会有这个，仅在 Android 设备出现，即使有这个报错也不影响的，所以不介意控制台报错的话是可以删掉这个 timeout 的
					 */
					this.videoEl.muted = true
					setTimeout(() => {
						this.videoEl.play()
						this.videoEl.muted = muted
						if (!autoplay) {
							setTimeout(() => {
								this.videoEl.pause()
							}, 100)
						}
					}, 10)
				}
			},
			triggerCommand(eventType) {
				if (eventType) {
					this.$ownerInstance.callMethod('resetEventCommand')
					this.videoEl && this.videoEl[eventType]()
				}
			},
			triggerFunc(func) {
				const {
					name,
					params
				} = func || {}
				if (name) {
					this[name](params)
					this.$ownerInstance.callMethod('resetFunc')
				}
			},
			removeHandler() {
				if (this.videoEl) {
					this.videoEl.pause()
					this.videoEl.src = ''
					this.$ownerInstance.callMethod('setViewData', {
						key: 'videoSrc',
						value: ''
					})
					this.videoEl.load()
				}
			},
			fullScreenHandler() {
				if (this.isApple()) {
					this.videoEl.webkitEnterFullscreen()
				} else {
					this.videoEl.requestFullscreen()
				}
			},
			toSeekHandler({
				sec,
				isDelay
			}) {
				const func = () => {
					if (this.videoEl) {
						this.videoEl.currentTime = sec
					}
				}

				// 延迟执行
				if (isDelay) {
					this.delayFunc = func
				} else {
					func()
				}
			},
			// 执行延迟函数
			execDelayFunc() {
				this.delayFunc && this.delayFunc()
				this.delayFunc = null
			},
			viewportChange(props) {
				this.renderProps = props
				const {
					autoplay,
					muted,
					controls,
					loop,
					playbackRate
				} = props
				if (this.videoEl) {
					this.videoEl.autoplay = autoplay
					this.videoEl.controls = controls
					this.videoEl.loop = loop
					this.videoEl.muted = muted
					this.videoEl.playbackRate = playbackRate
				}
			},
			randomNumChange(val) {
				this.num = val
			}
		}
	}
</script>

<style scoped>
	.player-wrapper {
		overflow: hidden;
		height: 100%;
		padding: 0;
		position: relative;
	}
</style>